package ar.com.e2solver.solver.operator.mutation.impl;

import java.util.ArrayList;
import java.util.List;

import ar.com.e2solver.exception.BusinessException;
import ar.com.e2solver.model.Coordinate;
import ar.com.e2solver.model.Piece;
import ar.com.e2solver.model.Puzzle;
import ar.com.e2solver.solver.Individual;
import ar.com.e2solver.solver.RandomConstants;
import ar.com.e2solver.solver.operator.OperatorUtil;
import ar.com.e2solver.solver.operator.mutation.MutationOperator;

public final class SwapAndRotateMutationOperator implements MutationOperator {
	
	private static SwapAndRotateMutationOperator instance = new SwapAndRotateMutationOperator();
	
	private SwapAndRotateMutationOperator(){}

	public static SwapAndRotateMutationOperator getInstance() {
		return instance;
	}

	public Individual operate(Individual individual) throws BusinessException {
		Puzzle puzzle = individual.getPuzzle().clone();
		
		Coordinate c1 = RandomConstants.getRandomCoordinate(puzzle.getRowsNumber(), puzzle.getColumnNumbers());
		Piece piece1 = puzzle.getPieceAt(c1);
		
		if(piece1.isACornerPiece()){
			List<Coordinate> coordinates = getCornerCoordinates(puzzle.getRowsNumber(), puzzle.getColumnNumbers()); 			
			coordinates.remove(c1);
			int randomIndex = RandomConstants.RANDOM.nextInt(coordinates.size());
			Coordinate c2 = coordinates.get(randomIndex);
			
			puzzle.swapPositions(c1, c2);
			
			// Acomoda los bordes de las esquinas
			OperatorUtil.rotateForCorner(c1, puzzle);
			OperatorUtil.rotateForCorner(c2, puzzle);

		} else if(piece1.isABorderPiece()){
			List<Coordinate> coordinates = getBorderCoordinates(puzzle.getRowsNumber(), puzzle.getColumnNumbers());
			coordinates.remove(c1);
			int randomIndex = RandomConstants.RANDOM.nextInt(coordinates.size());
			Coordinate c2 = coordinates.get(randomIndex);
			
			puzzle.swapPositions(c1, c2);
		
			// Acomoda los bordes de los bordes
			OperatorUtil.rotateForBorder(c1, puzzle);
			OperatorUtil.rotateForBorder(c2, puzzle);			

		} else {
			int x2Random = RandomConstants.RANDOM.nextInt(puzzle.getRowsNumber() - 2) + 1;
			int y2Random = RandomConstants.RANDOM.nextInt(puzzle.getColumnNumbers() - 2) + 1;
			Coordinate c2 = new Coordinate(x2Random, y2Random);
			puzzle.swapPositions(c1, c2);
			OperatorUtil.rotate(c1, puzzle);
			OperatorUtil.rotate(c2, puzzle);
		}
		
		return new Individual(puzzle);
	}

	
	private List<Coordinate> getCornerCoordinates(int row, int col) {
		List<Coordinate> coordinates = new ArrayList<Coordinate>();
		coordinates.add(new Coordinate(0,0));
		coordinates.add(new Coordinate(0, col -1));
		coordinates.add(new Coordinate(row -1, 0));
		coordinates.add(new Coordinate(row -1, col -1));
		return coordinates;
	}
	
	private List<Coordinate> getBorderCoordinates(int row, int col) {
		List<Coordinate> borderCoordinates = new ArrayList<Coordinate>();

		for(int i = 0; i < row; i++){
			borderCoordinates.add(new Coordinate(i, 0));
			borderCoordinates.add(new Coordinate(i, col - 1));
		}
		
		for(int j = 0; j < col; j++){
			borderCoordinates.add(new Coordinate(0, j));
			borderCoordinates.add(new Coordinate(row - 1, j));
		}
		
		// Saca las esquinas
		borderCoordinates.removeAll(getCornerCoordinates(row, col));
		
		return borderCoordinates;
	}	

}
